package com.example.springboot.springsecurity.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * springsecurity的配置类
 * 1.继承WebSecurityConfigurerAdapter
 * 2.开启@EnableWebSecurity
 * 3.在配置类中配置
 * @author abbys
 */
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 自定义配置定制请求的 授权规则
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //          / 请求不需要认证 level1下的资源要有VIP1角色才能访问 level2下的资源要有VIP2角色才能访问 ...
        //          默认访问需要权限才能访问的资源会自动跳到登录页面,让你去登录认证
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("VIP1")
                .antMatchers("/level2/**").hasRole("VIP2")
                .antMatchers("/level3/**").hasRole("VIP3");


        //springsecurity帮我们实现了认证 授权的一系列方法,且授权默认是基于角色的,以后业务中会有基于权限的跟用JWT结合security来做
        //目前这个项目中只做了最基本的 登录 注销 跟基于角色的权限控制

        
        //我们以前是自己写拦截器，配置拦截规则，写拦截器的拦截方法，基于session和cookie
        //对于 登录.退出.记住我 这种经常要做的方法springsecurity帮我们做了更好的实现我们只要开启就可以，且按照默认规则去使用即可
        //那么以后如果用springsecurity的话 登录 退出 记住我 就不用自己写了，直接配置然后开启即可


        //开启自动配置的表单登录功能  (默认的采用表单提交 且action:/login method:post 即是登录方法)
        //且会有默认页面,我们可以替换默认登录页,我们以前spring整合springsecurity的时候学过
        //如果登录失败会默认重定向到 /login?error这个地址
        //并且默认情况下 前端的用户名密码字段是 username 跟 password 当然可以修改
        //用户信息一般都是去查询数据库，但是我们也可以写死用户信息在配置类中  例子:重写configure(AuthenticationManagerBuilder auth) 看下面
        http.formLogin().
                //自定义登录页,我们这用了模板引擎,当你配置了loginPage后默认的登录url会变的跟loginPage的值一样，即登录请求从以前的/login变成/userlogin了
                loginPage("/userlogin");


        //开启自动配置的表单注销功能(退出登录)  (默认的采用表单提交 且action:/logout method:post 即是注销方法)
        //会帮你清空session中的用户信息，springsecurity底层也是用session保存用户信息
        //退出登录后默认会重定向到登录页面,可以修改 通过logoutSuccessUrl()
        http.logout().logoutSuccessUrl("/");    //退出成功后访问的url 在这个案例中就是返回到welcome.html,因为用了模板引擎


        //springsecurity还帮我们做了记住我功能,开启记住我 在你选了记住我以后底层也是把它存到cookie,且在注销后会帮你删除cookie
        //开启了这个后你会发现登录页面下自动多了个记住我的单选框,前提是我们使用security提供的默认的页面，前端字段默认是remember-me
        //如果我们自己定义了的登录页，想使用记住我功能 先开启，然后前端字段的name设置成remember_me即可，当然可以修改
        http.rememberMe().rememberMeParameter("remember");  //修改默认remember-me字段为remember

    }

    /**
     * 自定义配置定制请求的 认证规则
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //运行的时候在内存中生成一个 zhangsan用户 密码1234 这个用户有 VIP1 跟VIP2角色
        /*auth.inMemoryAuthentication()
                .withUser("zhangsan").password("1234").roles("VIP1","VIP2")
                .and()
                .withUser("lisi").password("1234").roles(" VIP2","VIP3")
                .and()
                .withUser("wangwu").password("1234").roles("VIP1","VIP3");*/

        //springboot 1.X 版本的时候上面那样写就可以了，但在springboot2.X版本后springsecurity规定密码必须得加密,不然会报错
        //记住定义多个的时候一定要用.and() 因为and是附加你不用and()的话可能会报UserDetailServiceNotFoundException 因为也许会覆盖
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("zhangsan").password(new BCryptPasswordEncoder().encode("1234")).roles("VIP1","VIP2")
                .and()
                .passwordEncoder(new BCryptPasswordEncoder()).withUser("lisi").password(new BCryptPasswordEncoder().encode("1234")).roles("VIP2","VIP3")
                .and()
                .passwordEncoder(new BCryptPasswordEncoder()).withUser("wangwu").password(new BCryptPasswordEncoder().encode("1234")).roles("VIP1","VIP3");
    }


}
